import java.io.File;
import java.io.IOException;
import java.io.OutputStream;

import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.io.ISVNEditor;
import org.tmatesoft.svn.core.io.ISVNReporter;
import org.tmatesoft.svn.core.io.ISVNReporterBaton;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.io.diff.SVNDeltaProcessor;
import org.tmatesoft.svn.core.io.diff.SVNDiffWindow;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

public class ExportZip {
	
	public class ExportReporterBaton implements ISVNReporterBaton {
	
	    private long exportRevision;
	        
	    public ExportReporterBaton( long revision ){
	        exportRevision = revision;
	    }
	        
	    public void report( ISVNReporter reporter ) throws SVNException {
	        try {
	            reporter.setPath( "" , null , exportRevision , true );
	            reporter.finishReport( );
	        } catch( SVNException svne ) {
	            reporter.abortReport( );
	            System.out.println( "Report failed" );
	        }
	    }
	}
	
	public class ExportEditor implements ISVNEditor{
	       
	    private File myZipFile;
	    private SVNDeltaProcessor myDeltaProcessor;
	    private ZipOutputStream myZipOut;
	   
	    public ExportEditor(File zipFile) {
	        myZipFile = zipFile;
	        myDeltaProcessor = new SVNDeltaProcessor();
	    }
	
	    public void targetRevision(long revision) throws SVNException {
	    }
	
	    public void openRoot(long revision) throws SVNException {
	    try{
	    myZipOut = new ZipOutputStream(
	                    new FileOutputStream(myZipFile));
	    }
	    catch(IOException ioe){
	    ioe.printStackTrace();
	    }
	    }
	   
	    public void addDir(String path, String copyFromPath, long copyFromRevision) throws SVNException {
	//        try{
	//        myZipOut.putNextEntry(new ZipEntry(path));
	//        }
	//        catch(IOException ioe){
	//    ioe.printStackTrace();
	//    }
	    }
	   
	    public void openDir(String path, long revision) throws SVNException {
	    }
	
	    public void changeDirProperty(String name, String value) throws SVNException {
	    }
	
	    public void addFile(String path, String copyFromPath, long copyFromRevision) throws SVNException {
	        try{
	        myZipOut.putNextEntry(new ZipEntry(path));
	        }
	        catch(IOException ioe){
	    ioe.printStackTrace();
	    }
	    }
	   
	    public void openFile(String path, long revision) throws SVNException {
	    }
	
	    public void changeFileProperty(String path, String name, String value) throws SVNException {
	    }        
	
	    public void applyTextDelta(String path, String baseChecksum) throws SVNException {
	    OutputStream stream = new FilterOutputStream(myZipOut) {
	      public void write(byte b[], int off, int len) throws IOException {
	        myZipOut.write(b, off, len);
	      }
	      public void close() {
	        // do nothing here.
	      }
	    };
	
	    myDeltaProcessor.applyTextDelta(null, stream, false);
	    }
	
	    public OutputStream textDeltaChunk(String path, SVNDiffWindow diffWindow)   throws SVNException {
	        return myDeltaProcessor.textDeltaChunk(diffWindow);
	    }
	   
	    public void textDeltaEnd(String path) throws SVNException {
	        myDeltaProcessor.textDeltaEnd();
	    }
	   
	    public void closeFile(String path, String textChecksum) throws SVNException {
	    try{
	        myZipOut.closeEntry();
	        }
	        catch(IOException ioe){
	    ioe.printStackTrace();
	    }
	    }
	
	    public void closeDir() throws SVNException {
	      try{
	        myZipOut.closeEntry();
	        }
	        catch(IOException ioe){
	    ioe.printStackTrace();
	    }
	    }
	
	    public void deleteEntry(String path, long revision) throws SVNException {
	    }
	   
	    public void absentDir(String path) throws SVNException {
	    }
	
	    public void absentFile(String path) throws SVNException {
	    }        
	   
	    public SVNCommitInfo closeEdit() throws SVNException {
	      try{
	        myZipOut.close();
	        }
	        catch(IOException ioe){
	    ioe.printStackTrace();
	    }
	        return null;
	    }
	   
	    public void abortEdit() throws SVNException {
	    }
	}
	
	public class Export {
	    
	    public static void main( String[] args ) { 
	        DAVRepositoryFactory.setup( );
	        SVNURL url = SVNURL.parseURIEncoded( "http://svn.svnkit.com/repos/svnkit/trunk/doc" );
	        String userName = "foo";
	        String userPassword = "bar";
	        
	        //Prepare filesystem directory (export destination).
	        File exportDir = new File( "export" );
	        if ( exportDir.exists( ) ) {
	            SVNErrorMessage err = SVNErrorMessage.create( SVNErrorCode.IO_ERROR , "Path ''{0}'' already exists" , exportDir );
	            throw new SVNException( err );
	        }
	        exportDir.mkdirs( );
	
	        SVNRepository repository = SVNRepositoryFactory.create( url );
	
	        ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager( userName, userPassword );
	        repository.setAuthenticationManager( authManager );
	
	        SVNNodeKind nodeKind = repository.checkPath( "" , -1 );
	        if ( nodeKind == SVNNodeKind.NONE ) {
	            SVNErrorMessage err = SVNErrorMessage.create( SVNErrorCode.UNKNOWN , "No entry at URL ''{0}''" , url );
	            throw new SVNException( err );
	        } else if ( nodeKind == SVNNodeKind.FILE ) {
	            SVNErrorMessage err = SVNErrorMessage.create( SVNErrorCode.UNKNOWN , "Entry at URL ''{0}'' is a file while directory was expected" , url );
	            throw new SVNException( err );
	        }
	
	        //Get latest repository revision. We will export repository contents at this very revision.
	        long latestRevision = repository.getLatestRevision( );
	        
	        ISVNReporterBaton reporterBaton = new ExportReporterBaton( latestRevision );
	        
	        ISVNEditor exportEditor = new ExportEditor( exportDir );
	        
	        /*
	         * Now ask SVNKit to perform generic 'update' operation using our reporter and editor.
	         * 
	         * We are passing:
	         * 
	         * - revision from which we would like to export
	         * - null as "target" name, to perform export from the URL SVNRepository was created for, 
	         *   not from some child directory.
	         * - reporterBaton
	         * - exportEditor.  
	         */
	        repository.update( latestRevision , null , true , reporterBaton , exportEditor );
	        
	        System.out.println( "Exported revision: " + latestRevision );
	    
	    }
	}
}
